/*
 * OrderLineItem.java
 *
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package org.pprun.hjpetstore.domain;

import java.io.Serializable;
import org.pprun.hjpetstore.persistence.CartItem;
import java.math.BigDecimal;
import java.util.Calendar;
import org.pprun.hjpetstore.persistence.IAuditable;

/**
 *
 * @author <a href="mailto:quest.run@gmail.com">pprun</a>
 */
public class OrderLineItem implements IAuditable, Serializable {

    /**
     * It’s critical that you implement equals() and hashCode() correctly, because
     * Hibernate relies on these methods for cache lookups. Identifier classes are also
     * expected to implement Serializable.
     */
    public static class Id implements Serializable {

        private int lineNumber;
        private long orderId;

        public Id() {
        }

        public Id(int lineNumber, long orderId) {
            this.lineNumber = lineNumber;
            this.orderId = orderId;
        }

        /**
         * @return the lineNumber
         */
        public int getLineNumber() {
            return lineNumber;
        }

        /**
         * @return the orderId
         */
        public long getOrderId() {
            return orderId;
        }

        /**
         * In general, we should not supply setter for composite-key,
         * but the issue we encountered is we can not set up the whole contract when initOrder,
         * the orderId is still not persisted, then it is null.
         * We have to set it lately once the order persisted.
         * @param orderId
         */
        public void setOrderId(long orderId) {
            this.orderId = orderId;
        }

        /**
         * You may have also noticed that the equals() and hashCode() methods always
         * access the properties of the “other” object via the getter methods. This is
         * extremely important, because the object instance passed as other may be a proxy
         * object, not the actual instance that holds the persistent state. To initialize this
         * proxy to get the property value, you need to access it with a getter method. This is
         * one point where Hibernate isn’t completely transparent. However, it’s a good prac-
         * tice to use getter methods instead of direct instance variable access anyway.
         * @param o
         * @return
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (!(o instanceof Id)) {
                return false;
            }

            Id that = (Id) o;

            return (this.getLineNumber() == that.getLineNumber()) && (this.getOrderId() == that.getOrderId());
        }

        @Override
        public int hashCode() {
            int result = 17;
            result = 31 * result + getLineNumber();
            result = 31 * result + (int) (getOrderId() ^ (getOrderId() >>> 32));
            return result;

        }

        @Override
        public String toString() {
            StringBuilder s = new StringBuilder();
            s.append("[");
            s.append("lineNumber=").append(getLineNumber());
            s.append(", ");
            s.append("orderId=").append(getOrderId());
            s.append("]");
            return s.toString();
        }
    }
    private Id id = new Id(); // eagerly initialize it then we can directly use it as id.keyField in the constructor
    private Long version;
    private int quantity;
    private BigDecimal unitPrice;
    private Calendar createTime = Calendar.getInstance();
    private Calendar updateTime;
    // associated object
    private Item item;
    private Order order;

    public OrderLineItem() {
    }

    public OrderLineItem initOrderLineItem(int lineNumber, Order order, CartItem cartItem) {
        assert order != null : "Order can not be null";

        id.lineNumber = lineNumber;

        // id.orderId will be set once order persisted - there's one of the reason we should not mapping composite key
        //id.orderId = order.getId();
        
        this.quantity = cartItem.getQuantity();
        this.unitPrice = cartItem.getItem().getListPrice();
        this.item = cartItem.getItem();
        this.order = order;

        return this;
    }

    public Id getId() {
        return id;
    }

    /**
     * On the other hand, you usually declare the setId() method private and let
     * Hibernate generate and set the identifier value. Or, you map it with direct field
     * access and implement only a getter method. (The exception to this rule is
     * classes with natural keys, where the value of the identifier is assigned by the
     * application before the object is made persistent instead of being generated by
     * Hibernate.
     * @param id
     */
    private void setId(Id id) {
        this.id = id;
    }

    public Item getItem() {
        return item;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    /**
     * @param unitPrice the unitPrice to set
     */
    public void setUnitPrice(BigDecimal unitPrice) {
        this.unitPrice = unitPrice;
    }

    public BigDecimal getUnitPrice() {
        return item.getListPrice();
    }

    /**
     * @return the createTime
     */
    @Override
    public Calendar getCreateTime() {
        return createTime;
    }

    private void setCreateTime(Calendar createTime) {
        this.createTime = createTime;
    }

    /**
     * @return the updateTime
     */
    @Override
    public Calendar getUpdateTime() {
        return updateTime;
    }

    private void setUpdateTime(Calendar updateTime) {
        this.updateTime = updateTime;
    }

    public BigDecimal getTotalPrice() {
        return this.getUnitPrice().multiply(BigDecimal.valueOf(quantity));
    }

    /**
     * @return the order
     */
    public Order getOrder() {
        return order;
    }

    @Override
    public String toString() {
        StringBuilder s = new StringBuilder(this.getClass().getSimpleName());
        s.append("[");
        s.append("id=").append(id);
        s.append(", ");
        s.append("version=").append(version);
        s.append(", ");
        s.append("item=").append(item);
        s.append(", ");
        s.append("unitPrice=").append(unitPrice);
        s.append(", ");
        s.append("quantity=").append(quantity);
        s.append("]");

        return s.toString();
    }
}


// Non-primary type implemenation for later reference
//    /**
//     * It’s critical that you implement equals() and hashCode() correctly, because
//     * Hibernate relies on these methods for cache lookups. Identifier classes are also
//     * expected to implement Serializable.
//     */
//    public static class Id implements Serializable {
//
//        private Long orderId;
//        private Integer lineNumber;
//
//        public Id() {
//        }
//
//        public Id(Long orderId, Integer lineNumber) {
//            this.orderId = orderId;
//            this.lineNumber = lineNumber;
//        }
//
//        // only Getters
//        /**
//         * @return the lineNumber
//         */
//        public int getLineNumber() {
//            return lineNumber;
//        }
//
//        /**
//         * @return the orderId
//         */
//        public long getOrderId() {
//            return orderId;
//        }
//
//        /**
//         * You may have also noticed that the equals() and hashCode() methods always
//         * access the properties of the “other” object via the getter methods. This is
//         * extremely important, because the object instance passed as other may be a proxy
//         * object, not the actual instance that holds the persistent state. <b>To initialize this
//         * proxy to get the property value, you need to access it with a getter method. This is
//         * one point where Hibernate isn’t completely transparent.</b> However, it’s a good prac-
//         * tice to use getter methods instead of direct instance variable access anyway.
//         * @param o
//         * @return
//         */
//        @Override
//        public boolean equals(Object o) {
//            if (this == o) {
//                return true;
//            }
//
//            if (!(o instanceof Id)) {
//                return false;
//            }
//
//            Id other = (Id) o;
//
//            return equals(this.orderId, other.getOrderId()) && equals(this.lineNumber, other.getLineNumber());
//        }
//
//        /**
//         * If we can make sure that the fields are not nullable, we can avoid to use this private method.
//         */
//        private static boolean equals(Object o1, Object o2) {
//            return o1 == null ? o2 == null : o1.equals(o2);
//        }
//
//        @Override
//        public int hashCode() {
//            return hashCode(orderId) ^ hashCode(lineNumber);
//        }
//
//        /**
//         * If we can make sure that the fields are not nullable, we can avoid to use this private method.
//         */
//        private static int hashCode(Object obj) {
//            return obj == null ? 0 : obj.hashCode();
//        }
//
//        @Override
//        public String toString() {
//            StringBuilder s = new StringBuilder();
//            s.append("[");
//            s.append("orderId=").append(orderId);
//            s.append(", ");
//            s.append("lineNumber=").append(lineNumber);
//            s.append("]");
//            return s.toString();
//        }
//    }

